/* 
1 获取页面的接口数据
  1 redux中获取当前城市信息
    1 如何在 组件的挂载完毕事件中 合理的获取到 当前的城市信息-异步请求回来 
      等待异步请求回来了 才渲染 组件 => 才会触发组件挂载完毕事件 
        在App.js 里面 针对  cityName 做一个条件渲染
          有值  才渲染  页面的所有内容
          没有值 不用渲染 
  2 热门城市接口中 获取 热门城市数据 

2 使用虚拟列表(长列表-微博)来改造 页面滚动

3 自己构建右侧的字母列表 定位 显示 

4 点击右侧的字母 
  1 激活选中

  2 左侧的城市列表 也可以根据被点击的元素的索引 进行滚动
    找文档 List 组件的文档就可以 
      1 根据索引来显示我们的长列表 scrollToIndex
        1 对齐方式  scrollToAlignment  = "start"
      2 根据高度来显示我们的长列表 

5 直接点击 字母列表 - Q  城市列表切换->N
  先滑动一次 再点击 就正常了
  原因:
    1 List 组件 有点懒   每一行的高度都不一样 
      正常的话 先很准确去计算一次索引和页面高度的关系 
    2 其实 List组件内部没有这么做
      一开始也会简单算一下(没有那么精确) 滑动了一次页面的时候 相当于 把所有的dom标签都渲染了一次
      把渲染后标签的高度记录下来 所以 
      我们滑动了一下之后 再 去点击 字母列表 它城市列表跳转就很准确 
    3 非受控表单 就解决了 不精确的问题 this.listRef.measureAllRows();

6 页面滚动 反过来 控制 字母列表 onRowsRendered

7 页面滚动的bug
  1 当用户 按住屏幕   页面上滑动的时候  会瞬间 往上滚动很远 
  2 原因
    1 当 页面滚动的时候 会将滚动索引 映射到 字母列表中
    2 改变字母列表的索引的时候 反过来 映射到 长列表中
    3 容易出现 滚动不准确的问题 
    4 无非就是 人家的组件的设计
      1 有它自己的设计的偏向
      2 存在bug 
  3 解决:
    1 长列表位置改变原理
      1 长列表的数据 宽度和高度 都设置过了 然后改变显示位置的话 就应该 
        不改变已经设置好的数据 
        只改变位置即可 => 人家 List 内部提供有!! 
      2 每一次索引改变的时候 这个长列表重新渲染一次  组件 render触发的时候(state 和 props 改变)
          也包含了 宽度 和高度 和 数据 以及 显示的索引 
    3 总结:
      1 把 List 组件上的属性 注释掉 // scrollToIndex={currentIndex}
      2 在字母列表点击事件中 调用  this.listRef.scrollToRow(currentIndex);
8  优化 组件完毕中的代码
   1 现象
      1 假如 第一个请求 花费了 3s 钟
      2 假如 第二个请求 花费了 10s 钟
      3 问: 一共花费了 13s 钟
      4 导致 请求花费的时间 是 叠加 而不是 最长的那个请求的时间(10s)
   2 优化:
      1 不需要按顺序来发送请求  可以同时发送多个请求出去
      2 等最长时间的请求回来 才做数据渲染-最后一个请求 Promise.all 

9 点击 长列表中的城市
  1 将被点击的城市 存到出 redux中
    1 思考一下 存什么的样式 数据格式进去  cityName 存一个对象  可以拿到城市名称 广州
    2 redux中的数据格式 
      { 
        center:{lng,lat}, 经纬度对象
        code, 百度地图的城市编码 - 后续
        level, 缩放的倍数 - 后续 
        name 城市的名称 
       }
    3 redux的流程
       1 action的类型 
       2 actionCreator 异步action
       3 在组件中 开始派发 触发action
       4 管理员中 来修改数据 
  2 返回上一页 

 */
import React, { Component } from 'react';
import { NavBar, Icon } from 'antd-mobile';

import { connect } from "react-redux";
import { request } from "../../utils/request";
import Css from "./index.module.scss";
import { List } from "react-virtualized";
import { actionUpateCity, actionClearCity } from "../../store/actionCreator";

class citySelect extends Component {

  state = {
    // 页面要渲染的城市数据  接口是一次性 返回 一次性就返回 10万条  ? 
    // 加入用户 真的一直往下滑动页面  最终 页面 内存:{1 : 10万数据-普通数组 2: 10万个dom元素 }
    // 1 10万数据-普通数组 不能动 
    // 2 通过代码的手段 让 页面 始终显示 30个dom元素
    //  2.1 当用户滑动页面了 我们再通过代码的手动 去渲染即将被显示的dom元素
    //      同时 也把 离开了用户屏幕的 dom元素 移除掉!! 
    finalList: [],
    // 字母列表
    letterList: [],
    // 字母激活索引
    currentIndex: 0
  }
  constructor() {
    super();
    // 创建一条线
    this.listRef = React.createRef();
  }
  // 组件的生命周期-挂载时 :
  // 1 constructor  2 render 3  componentDidMount
  async componentDidMount() {

    // 编码规范 声明变量的时候 尽量都放在 最上面 

    // 最终 页面可以直接渲染的城市数组
    let finalList = [];
    // 字母列表
    let letterList = ["#", "热"];

    // 1 获取当前的城市 
    const cityName = this.props.cityName;

    const res = await Promise.all([request.get("/area/hot"), request.get("/area/city?level=1")]);


    // 2 获取 热门城市
    const hots = res[0].data.body;
    // 3 获取所有城市 const 锁定的是 内存中的地址 明显的赋值语句 list=[23234]
    const allCitys = res[1].data.body;

    finalList.push({ name: "当前定位", children: [{ name: cityName }] });
    finalList.push({ name: "热门城市", children: hots.map(v => ({ name: v.label })) });

    // 4 对所有城市 排序  规则  按照 short 属性来排序 short :"bj" |  short :"aq"
    // 简单数据数组排序 easy 
    // 复杂数据数组排序 easy   隐式的改变 数组 
    allCitys.sort((a, b) => a.short < b.short ? -1 : 1);


    // 5 需要对 allCitys 进行循环
    allCitys.forEach(v => {
      // 5.1 获取 对象中的short 属性的第一个字母
      const firstLetter = v.short[0].toUpperCase();
      // 5.2 看一下 firstLetter="A" 是否存在于 最终的数组
      const index = finalList.findIndex(vv => vv.name === firstLetter);
      if (index === -1) {
        // 不存在 
        finalList.push({ name: firstLetter, children: [{ name: v.label }] })
        // console.log(firstLetter);
        letterList.push(firstLetter);
      } else {
        // 存在了
        finalList[index].children.push({ name: v.label })
      }
    })

    this.setState({ finalList, letterList });

    // measureAllRows  提取精确的计算一下 长列表的每一行的高度数据 
    // 方面点击 字母列表的时候 进行一个精确的跳转 
    // 非受控表单吗 video 播放器 好多方法 ref！！！
    // 把 List 看成是一个普通的标签即可
    this.listRef.measureAllRows();
    // console.log(this.listRef);
  }


  // 长列表的城市的点击事件
  handleCityItemClick = (cityName) => {
    // 清空当前城市信息 
    // this.props.handleClearCity();
    // // 异步action
    // this.props.handleUpdateCity(cityName);
    // // 返回上一页
    // // console.log(this.props.history);
    // this.props.history.goBack();
    // this.props.history.go(-1);

    this.props.handleUpdateCity(cityName)
      .then(() => {
        this.props.history.goBack()
      })
  }

  // 城市列表的渲染函数
  rowRenderer = ({ key, index, isScrolling, isVisible, style, }) => {
    const group = this.state.finalList[index];
    return (
      <div key={key} style={style} >
        <div className={Css.group_title}>{group.name}</div>
        <div className={Css.group_content}>
          {group.children.map(v => <div
            key={v.name}
            className={Css.city_item}
            onClick={() => this.handleCityItemClick(v.name)}  >{v.name}</div>)}
        </div>
      </div>
    )
  }
  // 计算每一行的高度
  rowHeight = ({ index }) => {

    // 40px => js 动态计算的方式 ? 
    // 数字 =>单位 px
    // rem 也是可以 转为px 
    // 40 * 10rem / 375 = 40 * document.documentElement.style.fontSize / 375 
    // 1 rem = 多少px
    // 1 rem = html标签的fontsize
    // 10 rem = 10倍的html标签的字体大小
    // return this.state.finalList[index].children.length * 40 + 40


    
    // 1 rem = window.screen.width / 10
    // 10 rem =  window.screen.width

    // 计算方式 一
    // const rate = 10 * parseFloat(document.documentElement.style.fontSize) / 375;
    // 计算方式 二 
    const rate = window.screen.width / 375;

    // rem 计算 方式有2
    // 1 通过  1 rem =  根标签的字体大小
    // 2 通过  1 rem =  屏幕的十分之一  因为 淘宝flexible的作用 

 
    //  如何通过 数字(px)  来表示 之前的比例关系 40 * 10rem / 375
    return this.state.finalList[index].children.length * 40 * rate + 40 * rate

  }

  // 点击 字母列表
  handleLetterClick = (currentIndex) => {
    this.setState({ currentIndex });
    // 调用 List 的一个方法 
    this.listRef.scrollToRow(currentIndex);
  }

  // 行滚动完毕事件
  onRowsRendered = ({ startIndex }) => {
    if (this.state.currentIndex === startIndex) return;
    // 只会改变右侧字母列表的 索引而已
    this.setState({ currentIndex: startIndex });
  }

  render() {
    const { finalList, letterList, currentIndex } = this.state;
    return (
      <div className={Css.city_select} >
        {/* 1.0 顶部 导航栏 开始 */}
        <NavBar
          mode="light"
          icon={<Icon type="left" />}
          onLeftClick={() => this.props.history.goBack()}
        >城市选择</NavBar>
        {/* 1.0 顶部 导航栏 结束 */}
        {/* 2.0 内容 开始 */}
        <div className={Css.city_select_content}>
          {/* 2.1 主要城市 开始 */}
          <div className={Css.main_list}>
            <List
              // List的宽度  屏幕的宽度 
              width={window.screen.width}
              // List的高度
              height={window.screen.height - 45}
              // 数组的长度
              rowCount={finalList.length}
              // 每一行的高度  默认值=> 默认值等于?  
              // 大行的高度 置顶一个数值   我们自己定义 大行的高度
              rowHeight={this.rowHeight}
              // 如何渲染每一行标签 
              rowRenderer={this.rowRenderer}
              // 滚动到索引 
              // scrollToIndex={currentIndex}
              // 设置 列表的索引的对齐位置
              scrollToAlignment="start"
              // 赋值 ref
              ref={(ref) => this.listRef = ref}
              // 行渲染完毕事件 (参数-滚动到的索引)
              onRowsRendered={this.onRowsRendered}
            />
          </div>
          {/* 2.1 主要城市 结束 */}

          {/* 2.2 字母列表 开始 */}
          <div className={Css.letter_view}>
            {letterList.map((v, i) =>
              <div key={v}
                onClick={() => this.handleLetterClick(i)}
                className={[
                  Css.letter_item,
                  i === currentIndex ? Css.active : ''
                ].join(' ')} >
                <span>{v}</span>
              </div>)}
          </div>
          {/* 2.2 字母列表 结束 */}
        </div>
        {/* 2.0 内容 结束 */}
      </div>
    );
  }
}


const mapStateToProps = (state) => {
  return {
    cityName: state.mapReducer.city.name
  }
}

const mapDispatchToProps = (dispatch) => {
  return {
    handleUpdateCity(cityName) {
      // dispatch 这个方法的返回值 是 promise对象 
      return dispatch(actionUpateCity(cityName));
    },
    // 清空城市信息
    handleClearCity() {
      dispatch(actionClearCity());
    }
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(citySelect);